Clover icon

compiler

  1. Project Clover database Thu Jan 12 2023 14:04:05 MST
  2. Package com.google.javascript.jscomp

File Compiler.java

 

Coverage histogram

../../../../img/srcFileCovDistChart8.png
75% of files have more coverage

Code metrics

280
811
176
3
2,552
1,708
349
0.43
4.61
58.67
1.98

Classes

Class Line # Actions
Compiler 83 795 339 355
0.713478671.3%
Compiler.CodeBuilder 1815 16 9 6
0.777777877.8%
Compiler.IntermediateState 2282 0 1 1
0.00%
 

Contributing tests

This file is covered by 1942 tests. .

Source view

1    /*
2    * Copyright 2004 The Closure Compiler Authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10    * Unless required by applicable law or agreed to in writing, software
11    * distributed under the License is distributed on an "AS IS" BASIS,
12    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13    * See the License for the specific language governing permissions and
14    * limitations under the License.
15    */
16   
17    package com.google.javascript.jscomp;
18   
19    import com.google.common.annotations.VisibleForTesting;
20    import com.google.common.base.Charsets;
21    import com.google.common.base.Preconditions;
22    import com.google.common.base.Supplier;
23    import com.google.common.base.Throwables;
24    import com.google.common.collect.Lists;
25    import com.google.common.collect.Maps;
26    import com.google.common.io.CharStreams;
27    import com.google.javascript.jscomp.CompilerOptions.DevMode;
28    import com.google.javascript.jscomp.CompilerOptions.LanguageMode;
29    import com.google.javascript.jscomp.ReferenceCollectingCallback.ReferenceCollection;
30    import com.google.javascript.jscomp.Scope.Var;
31    import com.google.javascript.jscomp.deps.SortedDependencies;
32    import com.google.javascript.jscomp.deps.SortedDependencies.CircularDependencyException;
33    import com.google.javascript.jscomp.deps.SortedDependencies.MissingProvideException;
34    import com.google.javascript.jscomp.parsing.Config;
35    import com.google.javascript.jscomp.parsing.ParserRunner;
36    import com.google.javascript.jscomp.type.ChainableReverseAbstractInterpreter;
37    import com.google.javascript.jscomp.type.ClosureReverseAbstractInterpreter;
38    import com.google.javascript.jscomp.type.ReverseAbstractInterpreter;
39    import com.google.javascript.jscomp.type.SemanticReverseAbstractInterpreter;
40    import com.google.javascript.rhino.IR;
41    import com.google.javascript.rhino.InputId;
42    import com.google.javascript.rhino.JSDocInfo;
43    import com.google.javascript.rhino.Node;
44    import com.google.javascript.rhino.Token;
45    import com.google.javascript.rhino.head.ErrorReporter;
46    import com.google.javascript.rhino.head.ast.AstRoot;
47    import com.google.javascript.rhino.jstype.JSTypeRegistry;
48   
49    import java.io.IOException;
50    import java.io.InputStreamReader;
51    import java.io.PrintStream;
52    import java.io.Serializable;
53    import java.util.Collections;
54    import java.util.HashMap;
55    import java.util.List;
56    import java.util.Map;
57    import java.util.ResourceBundle;
58    import java.util.Set;
59    import java.util.concurrent.Callable;
60    import java.util.concurrent.ExecutionException;
61    import java.util.concurrent.ExecutorService;
62    import java.util.concurrent.Executors;
63    import java.util.concurrent.ThreadFactory;
64    import java.util.logging.Level;
65    import java.util.logging.Logger;
66    import java.util.regex.Matcher;
67   
68    /**
69    * Compiler (and the other classes in this package) does the following:
70    * <ul>
71    * <li>parses JS code
72    * <li>checks for undefined variables
73    * <li>performs optimizations such as constant folding and constants inlining
74    * <li>renames variables (to short names)
75    * <li>outputs compact JavaScript code
76    * </ul>
77    *
78    * External variables are declared in 'externs' files. For instance, the file
79    * may include definitions for global javascript/browser objects such as
80    * window, document.
81    *
82    */
 
83    public class Compiler extends AbstractCompiler {
84    static final String SINGLETON_MODULE_NAME = "[singleton]";
85   
86    static final DiagnosticType MODULE_DEPENDENCY_ERROR =
87    DiagnosticType.error("JSC_MODULE_DEPENDENCY_ERROR",
88    "Bad dependency: {0} -> {1}. "
89    + "Modules must be listed in dependency order.");
90   
91    static final DiagnosticType MISSING_ENTRY_ERROR = DiagnosticType.error(
92    "JSC_MISSING_ENTRY_ERROR",
93    "required entry point \"{0}\" never provided");
94   
95    private static final String CONFIG_RESOURCE =
96    "com.google.javascript.jscomp.parsing.ParserConfig";
97   
98    CompilerOptions options = null;
99   
100    private PassConfig passes = null;
101   
102    // The externs inputs
103    private List<CompilerInput> externs;
104   
105    // The JS source modules
106    private List<JSModule> modules;
107   
108    // The graph of the JS source modules. Must be null if there are less than
109    // 2 modules, because we use this as a signal for which passes to run.
110    private JSModuleGraph moduleGraph;
111   
112    // The JS source inputs
113    private List<CompilerInput> inputs;
114   
115    // error manager to which error management is delegated
116    private ErrorManager errorManager;
117   
118    // Warnings guard for filtering warnings.
119    private WarningsGuard warningsGuard;
120   
121    // Compile-time injected libraries. The node points to the last node of
122    // the library, so code can be inserted after.
123    private final Map<String, Node> injectedLibraries = Maps.newLinkedHashMap();
124   
125    // Parse tree root nodes
126    Node externsRoot;
127    Node jsRoot;
128    Node externAndJsRoot;
129   
130    private Map<InputId, CompilerInput> inputsById;
131   
132    /** The source code map */
133    private SourceMap sourceMap;
134   
135    /** The externs created from the exports. */
136    private String externExports = null;
137   
138    /**
139    * Ids for function inlining so that each declared name remains
140    * unique.
141    */
142    private int uniqueNameId = 0;
143   
144    /**
145    * Whether to assume there are references to the RegExp Global object
146    * properties.
147    */
148    private boolean hasRegExpGlobalReferences = true;
149   
150    /** The function information map */
151    private FunctionInformationMap functionInformationMap;
152   
153    /** Debugging information */
154    private final StringBuilder debugLog = new StringBuilder();
155   
156    /** Detects Google-specific coding conventions. */
157    CodingConvention defaultCodingConvention = new ClosureCodingConvention();
158   
159    private JSTypeRegistry typeRegistry;
160    private Config parserConfig = null;
161   
162    private ReverseAbstractInterpreter abstractInterpreter;
163    private TypeValidator typeValidator;
164   
165    public PerformanceTracker tracker;
166   
167    // The oldErrorReporter exists so we can get errors from the JSTypeRegistry.
168    private final com.google.javascript.rhino.ErrorReporter oldErrorReporter =
169    RhinoErrorReporter.forOldRhino(this);
170   
171    // This error reporter gets the messages from the current Rhino parser.
172    private final ErrorReporter defaultErrorReporter =
173    RhinoErrorReporter.forNewRhino(this);
174   
175    /** Error strings used for reporting JSErrors */
176    public static final DiagnosticType OPTIMIZE_LOOP_ERROR = DiagnosticType.error(
177    "JSC_OPTIMIZE_LOOP_ERROR",
178    "Exceeded max number of optimization iterations: {0}");
179    public static final DiagnosticType MOTION_ITERATIONS_ERROR =
180    DiagnosticType.error("JSC_OPTIMIZE_LOOP_ERROR",
181    "Exceeded max number of code motion iterations: {0}");
182   
183    // We use many recursive algorithms that use O(d) memory in the depth
184    // of the tree.
185    private static final long COMPILER_STACK_SIZE = (1 << 21); // About 2MB
186   
187    /**
188    * Under JRE 1.6, the JS Compiler overflows the stack when running on some
189    * large or complex JS code. When threads are available, we run all compile
190    * jobs on a separate thread with a larger stack.
191    *
192    * That way, we don't have to increase the stack size for *every* thread
193    * (which is what -Xss does).
194    */
195    private static final ExecutorService compilerExecutor =
196    Executors.newCachedThreadPool(new ThreadFactory() {
 
197  2 toggle @Override public Thread newThread(Runnable r) {
198  2 return new Thread(null, r, "jscompiler", COMPILER_STACK_SIZE);
199    }
200    });
201   
202    /**
203    * Use a dedicated compiler thread per Compiler instance.
204    */
205    private Thread compilerThread = null;
206   
207    /** Whether to use threads. */
208    private boolean useThreads = true;
209   
210   
211    /**
212    * Logger for the whole com.google.javascript.jscomp domain -
213    * setting configuration for this logger affects all loggers
214    * in other classes within the compiler.
215    */
216    private static final Logger logger =
217    Logger.getLogger("com.google.javascript.jscomp");
218   
219    private final PrintStream outStream;
220   
221    private GlobalVarReferenceMap globalRefMap = null;
222   
223    private volatile double progress = 0.0;
224    private String lastPassName;
225   
226    /**
227    * Creates a Compiler that reports errors and warnings to its logger.
228    */
 
229  20331 toggle public Compiler() {
230  20331 this((PrintStream) null);
231    }
232   
233    /**
234    * Creates n Compiler that reports errors and warnings to an output
235    * stream.
236    */
 
237  20447 toggle public Compiler(PrintStream stream) {
238  20447 addChangeHandler(recentChange);
239  20447 outStream = stream;
240    }
241   
242    /**
243    * Creates a Compiler that uses a custom error manager.
244    */
 
245  1 toggle public Compiler(ErrorManager errorManager) {
246  1 this();
247  1 setErrorManager(errorManager);
248    }
249   
250    /**
251    * Sets the error manager.
252    *
253    * @param errorManager the error manager, it cannot be {@code null}
254    */
 
255  31122 toggle public void setErrorManager(ErrorManager errorManager) {
256  31122 Preconditions.checkNotNull(
257    errorManager, "the error manager cannot be null");
258  31122 this.errorManager = errorManager;
259    }
260   
261    /**
262    * Creates a message formatter instance corresponding to the value of
263    * {@link CompilerOptions}.
264    */
 
265  20158 toggle private MessageFormatter createMessageFormatter() {
266  20158 boolean colorize = options.shouldColorizeErrorOutput();
267  20158 return options.errorFormat.toFormatter(this, colorize);
268    }
269   
270    /**
271    * Initialize the compiler options. Only necessary if you're not doing
272    * a normal compile() job.
273    */
 
274  20399 toggle public void initOptions(CompilerOptions options) {
275  20399 this.options = options;
276  20399 if (errorManager == null) {
277  20158 if (outStream == null) {
278  20044 setErrorManager(
279    new LoggerErrorManager(createMessageFormatter(), logger));
280    } else {
281  114 PrintStreamErrorManager printer =
282    new PrintStreamErrorManager(createMessageFormatter(), outStream);
283  114 printer.setSummaryDetailLevel(options.summaryDetailLevel);
284  114 setErrorManager(printer);
285    }
286    }
287   
288    // DiagnosticGroups override the plain checkTypes option.
289  20399 if (options.enables(DiagnosticGroups.CHECK_TYPES)) {
290  18381 options.checkTypes = true;
291  2018 } else if (options.disables(DiagnosticGroups.CHECK_TYPES)) {
292  3 options.checkTypes = false;
293  2015 } else if (!options.checkTypes) {
294    // If DiagnosticGroups did not override the plain checkTypes
295    // option, and checkTypes is enabled, then turn off the
296    // parser type warnings.
297  1968 options.setWarningLevel(
298    DiagnosticGroup.forType(
299    RhinoErrorReporter.TYPE_PARSE_ERROR),
300    CheckLevel.OFF);
301    }
302   
303  20399 if (options.checkGlobalThisLevel.isOn() &&
304    !options.disables(DiagnosticGroups.GLOBAL_THIS)) {
305  60 options.setWarningLevel(
306    DiagnosticGroups.GLOBAL_THIS,
307    options.checkGlobalThisLevel);
308    }
309   
310  20399 if (options.getLanguageIn() == LanguageMode.ECMASCRIPT5_STRICT) {
311  5 options.setWarningLevel(
312    DiagnosticGroups.ES5_STRICT,
313    CheckLevel.ERROR);
314    }
315   
316    // Initialize the warnings guard.
317  20399 List<WarningsGuard> guards = Lists.newArrayList();
318  20399 guards.add(
319    new SuppressDocWarningsGuard(
320    getDiagnosticGroups().getRegisteredGroups()));
321  20399 guards.add(options.getWarningsGuard());
322   
323  20399 ComposeWarningsGuard composedGuards = new ComposeWarningsGuard(guards);
324   
325    // All passes must run the variable check. This synthesizes
326    // variables later so that the compiler doesn't crash. It also
327    // checks the externs file for validity. If you don't want to warn
328    // about missing variable declarations, we shut that specific
329    // error off.
330  20399 if (!options.checkSymbols &&
331    !composedGuards.enables(DiagnosticGroups.CHECK_VARIABLES)) {
332  2534 composedGuards.addGuard(new DiagnosticGroupWarningsGuard(
333    DiagnosticGroups.CHECK_VARIABLES, CheckLevel.OFF));
334    }
335   
336  20399 this.warningsGuard = composedGuards;
337    }
338   
339    /**
340    * Initializes the instance state needed for a compile job.
341    * @deprecated Convert your arrays to lists and use the list-based API.
342    */
 
343  0 toggle @Deprecated
344    public void init(JSSourceFile[] externs, JSSourceFile[] inputs,
345    CompilerOptions options) {
346  0 init(Lists.<JSSourceFile>newArrayList(externs),
347    Lists.<JSSourceFile>newArrayList(inputs), options);
348    }
349   
350    /**
351    * Initializes the instance state needed for a compile job.
352    */
 
353  18381 toggle public <T1 extends SourceFile, T2 extends SourceFile> void init(
354    List<T1> externs,
355    List<T2> inputs,
356    CompilerOptions options) {
357  18381 JSModule module = new JSModule(SINGLETON_MODULE_NAME);
358  18381 for (SourceFile input : inputs) {
359  18517 module.add(input);
360    }
361   
362  18381 initModules(externs, Lists.newArrayList(module), options);
363    }
364   
365    /**
366    * Initializes the instance state needed for a compile job if the sources
367    * are in modules.
368    * @deprecated Convert your arrays to lists and use the list-based API.
369    */
 
370  0 toggle @Deprecated
371    public void init(JSSourceFile[] externs, JSModule[] modules,
372    CompilerOptions options) {
373  0 initModules(Lists.<SourceFile>newArrayList(externs),
374    Lists.<JSModule>newArrayList(modules), options);
375    }
376   
377    /**
378    * Initializes the instance state needed for a compile job if the sources
379    * are in modules.
380    */
 
381  18562 toggle public <T extends SourceFile> void initModules(
382    List<T> externs, List<JSModule> modules, CompilerOptions options) {
383  18562 initOptions(options);
384   
385  18562 checkFirstModule(modules);
386  18562 fillEmptyModules(modules);
387   
388  18562 this.externs = makeCompilerInput(externs, true);
389   
390    // Generate the module graph, and report any errors in the module
391    // specification as errors.
392  18562 this.modules = modules;
393  18562 if (modules.size() > 1) {
394  57 try {
395  57 this.moduleGraph = new JSModuleGraph(modules);
396    } catch (JSModuleGraph.ModuleDependenceException e) {
397    // problems with the module format. Report as an error. The
398    // message gives all details.
399  0 report(JSError.make(MODULE_DEPENDENCY_ERROR,
400    e.getModule().getName(), e.getDependentModule().getName()));
401  0 return;
402    }
403    } else {
404  18505 this.moduleGraph = null;
405    }
406   
407  18562 this.inputs = getAllInputsFromModules(modules);
408  18562 initBasedOnOptions();
409   
410  18562 initInputsByIdMap();
411    }
412   
413    /**
414    * Do any initialization that is dependent on the compiler options.
415    */
 
416  18562 toggle private void initBasedOnOptions() {
417    // Create the source map if necessary.
418  18562 if (options.sourceMapOutputPath != null) {
419  22 sourceMap = options.sourceMapFormat.getInstance();
420  22 sourceMap.setPrefixMappings(options.sourceMapLocationMappings);
421    }
422    }
423   
 
424  18562 toggle private <T extends SourceFile> List<CompilerInput> makeCompilerInput(
425    List<T> files, boolean isExtern) {
426  18562 List<CompilerInput> inputs = Lists.newArrayList();
427  18562 for (T file : files) {
428  9011 inputs.add(new CompilerInput(file, isExtern));
429    }
430  18562 return inputs;
431    }
432   
433    private static final DiagnosticType EMPTY_MODULE_LIST_ERROR =
434    DiagnosticType.error("JSC_EMPTY_MODULE_LIST_ERROR",
435    "At least one module must be provided");
436   
437    private static final DiagnosticType EMPTY_ROOT_MODULE_ERROR =
438    DiagnosticType.error("JSC_EMPTY_ROOT_MODULE_ERROR",
439    "Root module '{0}' must contain at least one source code input");
440   
441    /**
442    * Verifies that at least one module has been provided and that the first one
443    * has at least one source code input.
444    */
 
445  18562 toggle private void checkFirstModule(List<JSModule> modules) {
446  18562 if (modules.isEmpty()) {
447  0 report(JSError.make(EMPTY_MODULE_LIST_ERROR));
448  18562 } else if (modules.get(0).getInputs().isEmpty() && modules.size() > 1) {
449    // The root module may only be empty if there is exactly 1 module.
450  0 report(JSError.make(EMPTY_ROOT_MODULE_ERROR,
451    modules.get(0).getName()));
452    }
453    }
454   
455    /**
456    * Empty modules get an empty "fill" file, so that we can move code into
457    * an empty module.
458    */
 
459  25 toggle static String createFillFileName(String moduleName) {
460  25 return "[" + moduleName + "]";
461    }
462   
463    /**
464    * Fill any empty modules with a place holder file. It makes any cross module
465    * motion easier.
466    */
 
467  18674 toggle private static void fillEmptyModules(List<JSModule> modules) {
468  18674 for (JSModule module : modules) {
469  18761 if (module.getInputs().isEmpty()) {
470  25 module.add(SourceFile.fromCode(
471    createFillFileName(module.getName()), ""));
472    }
473    }
474    }
475   
476    /**
477    * Rebuilds the internal list of inputs by iterating over all modules.
478    * This is necessary if inputs have been added to or removed from a module
479    * after the {@link #init(List, List, CompilerOptions)} call.
480    */
 
481  112 toggle public void rebuildInputsFromModules() {
482  112 inputs = getAllInputsFromModules(modules);
483  112 initInputsByIdMap();
484    }
485   
486    /**
487    * Builds a single list of all module inputs. Verifies that it contains no
488    * duplicates.
489    */
 
490  18674 toggle private static List<CompilerInput> getAllInputsFromModules(
491    List<JSModule> modules) {
492  18674 List<CompilerInput> inputs = Lists.newArrayList();
493  18674 Map<String, JSModule> inputMap = Maps.newHashMap();
494  18674 for (JSModule module : modules) {
495  18761 for (CompilerInput input : module.getInputs()) {
496  18923 String inputName = input.getName();
497   
498    // NOTE(nicksantos): If an input is in more than one module,
499    // it will show up twice in the inputs list, and then we
500    // will get an error down the line.
501  18923 inputs.add(input);
502  18923 inputMap.put(inputName, module);
503    }
504    }
505  18674 return inputs;
506    }
507   
508    static final DiagnosticType DUPLICATE_INPUT =
509    DiagnosticType.error("JSC_DUPLICATE_INPUT", "Duplicate input: {0}");
510    static final DiagnosticType DUPLICATE_EXTERN_INPUT =
511    DiagnosticType.error("JSC_DUPLICATE_EXTERN_INPUT",
512    "Duplicate extern input: {0}");
513   
514    /**
515    * Creates a map to make looking up an input by name fast. Also checks for
516    * duplicate inputs.
517    */
 
518  18674 toggle void initInputsByIdMap() {
519  18674 inputsById = new HashMap<InputId, CompilerInput>();
520  18674 for (CompilerInput input : externs) {
521  9123 InputId id = input.getInputId();
522  9123 CompilerInput previous = putCompilerInput(id, input);
523  9123 if (previous != null) {
524  0 report(JSError.make(DUPLICATE_EXTERN_INPUT, input.getName()));
525    }
526    }
527  18674 for (CompilerInput input : inputs) {
528  18923 InputId id = input.getInputId();
529  18923 CompilerInput previous = putCompilerInput(id, input);
530  18923 if (previous != null) {
531  0 report(JSError.make(DUPLICATE_INPUT, input.getName()));
532    }
533    }
534    }
535   
 
536  1 toggle public Result compile(
537    SourceFile extern, SourceFile input, CompilerOptions options) {
538  1 return compile(Lists.newArrayList(extern), Lists.newArrayList(input), options);
539    }
540   
541    /**
542    * @deprecated Convert your arrays to lists and use the list-based API.
543    */
 
544  0 toggle @Deprecated
545    public Result compile(
546    SourceFile extern, JSSourceFile[] input, CompilerOptions options) {
547  0 return compile(Lists.newArrayList(extern), Lists.newArrayList(input), options);
548    }
549   
550    /**
551    * @deprecated Convert your arrays to lists and use the list-based
552    * compileModules method.
553    */
 
554  0 toggle @Deprecated
555    public Result compile(
556    JSSourceFile extern, JSModule[] modules, CompilerOptions options) {
557  0 return compileModules(
558    Lists.newArrayList(extern), Lists.newArrayList(modules), options);
559    }
560   
561    /**
562    * Compiles a list of inputs.
563    * @deprecated Convert your arrays to lists and use the list-based compile
564    * method.
565    */
 
566  0 toggle @Deprecated
567    public Result compile(JSSourceFile[] externs,
568    JSSourceFile[] inputs,
569    CompilerOptions options) {
570  0 return compile(Lists.<SourceFile>newArrayList(externs),
571    Lists.<SourceFile>newArrayList(inputs),
572    options);
573    }
574   
575    /**
576    * Compiles a list of inputs.
577    */
 
578  115 toggle public <T1 extends SourceFile, T2 extends SourceFile> Result compile(
579    List<T1> externs, List<T2> inputs, CompilerOptions options) {
580    // The compile method should only be called once.
581  115 Preconditions.checkState(jsRoot == null);
582   
583  115 try {
584  115 init(externs, inputs, options);
585  115 if (hasErrors()) {
586  0 return getResult();
587    }
588  115 return compile();
589    } finally {
590  115 Tracer t = newTracer("generateReport");
591  115 errorManager.generateReport();
592  115 stopTracer(t, "generateReport");
593    }
594    }
595   
596    /**
597    * Compiles a list of modules.
598    * @deprecated Convert your arrays to lists and use the list-based
599    * compileModules method.
600    */
 
601  0 toggle @Deprecated
602    public Result compile(JSSourceFile[] externs,
603    JSModule[] modules,
604    CompilerOptions options) {
605  0 return compileModules(Lists.<SourceFile>newArrayList(externs),
606    Lists.<JSModule>newArrayList(modules),
607    options);
608    }
609   
610    /**
611    * Compiles a list of modules.
612    */
 
613  129 toggle public <T extends SourceFile> Result compileModules(List<T> externs,
614    List<JSModule> modules, CompilerOptions options) {
615    // The compile method should only be called once.
616  129 Preconditions.checkState(jsRoot == null);
617   
618  129 try {
619  129 initModules(externs, modules, options);
620  129 if (hasErrors()) {
621  0 return getResult();
622    }
623  129 return compile();
624    } finally {
625  129 Tracer t = newTracer("generateReport");
626  129 errorManager.generateReport();
627  129 stopTracer(t, "generateReport");
628    }
629    }
630   
 
631  244 toggle private Result compile() {
632  244 return runInCompilerThread(new Callable<Result>() {
 
633  244 toggle @Override
634    public Result call() throws Exception {
635  244 compileInternal();
636  243 return getResult();
637    }
638    });
639    }
640   
641    /**
642    * Disable threads. This is for clients that run on AppEngine and
643    * don't have threads.
644    */
 
645  0 toggle public void disableThreads() {
646  0 useThreads = false;
647    }
648   
 
649  442 toggle @SuppressWarnings("unchecked")
650    <T> T runInCompilerThread(final Callable<T> callable) {
651  442 final boolean dumpTraceReport = options != null && options.tracer.isOn();
652  442 T result = null;
653  442 final Throwable[] exception = new Throwable[1];
654  442 Callable<T> bootCompilerThread = new Callable<T>() {
 
655  335 toggle @Override
656    public T call() {
657  335 try {
658  335 compilerThread = Thread.currentThread();
659  335 if (dumpTraceReport) {
660  0 Tracer.initCurrentThreadTrace();
661    }
662  335 return callable.call();
663    } catch (Throwable e) {
664  1 exception[0] = e;
665    } finally {
666  335 compilerThread = null;
667  335 if (dumpTraceReport) {
668  0 Tracer.logAndClearCurrentThreadTrace();
669  0 tracker.outputTracerReport(outStream == null ?
670    System.out : outStream);
671    }
672    }
673  1 return null;
674    }
675    };
676   
677  442 Preconditions.checkState(
678    compilerThread == null || compilerThread == Thread.currentThread(),
679    "Please do not share the Compiler across threads");
680   
681    // If the compiler thread is available, use it.
682  442 if (useThreads && compilerThread == null) {
683  335 try {
684  335 result = compilerExecutor.submit(bootCompilerThread).get();
685    } catch (InterruptedException e) {
686  0 throw Throwables.propagate(e);
687    } catch (ExecutionException e) {
688  0 throw Throwables.propagate(e);
689    }
690    } else {
691  107 try {
692  107 result = callable.call();
693    } catch (Exception e) {
694  0 exception[0] = e;
695    }
696    }
697   
698    // Pass on any exception caught by the runnable object.
699  442 if (exception[0] != null) {
700  1 throw new RuntimeException(exception[0]);
701    }
702   
703  441 return result;
704    }
705   
 
706  244 toggle private void compileInternal() {
707  244 setProgress(0.0, null);
708  244 parse();
709    // 15 percent of the work is assumed to be for parsing (based on some
710    // minimal analysis on big JS projects, of course this depends on options)
711  244 setProgress(0.15, "parse");
712  244 if (hasErrors()) {
713  6 return;
714    }
715   
716  238 if (!precheck()) {
717  0 return;
718    }
719   
720  238 if (options.nameAnonymousFunctionsOnly) {
721    // TODO(nicksantos): Move this into an instrument() phase maybe?
722  0 check();
723  0 return;
724    }
725   
726  238 if (!options.skipAllPasses) {
727  236 check();
728  236 if (hasErrors()) {
729  7 return;
730    }
731   
732  229 if (options.isExternExportsEnabled()
733    || options.externExportsPath != null) {
734  10 externExports();
735    }
736   
737    // IDE-mode is defined to stop here, before the heavy rewriting begins.
738  229 if (!options.ideMode) {
739  176 optimize();
740    }
741    }
742   
743  230 if (options.recordFunctionInformation) {
744  0 recordFunctionInformation();
745    }
746   
747  230 if (options.devMode == DevMode.START_AND_END) {
748  0 runSanityCheck();
749    }
750  230 setProgress(1.0, "recordFunctionInformation");
751    }
752   
 
753  246 toggle public void parse() {
754  246 parseInputs();
755    }
756   
 
757  735 toggle PassConfig getPassConfig() {
758  735 if (passes == null) {
759  247 passes = createPassConfigInternal();
760    }
761  735 return passes;
762    }
763   
764    /**
765    * Create the passes object. Clients should use setPassConfig instead of
766    * overriding this.
767    */
 
768  247 toggle PassConfig createPassConfigInternal() {
769  247 return new DefaultPassConfig(options);
770    }
771   
772    /**
773    * @param passes The PassConfig to use with this Compiler.
774    * @throws NullPointerException if passes is null
775    * @throws IllegalStateException if this.passes has already been assigned
776    */
 
777  0 toggle public void setPassConfig(PassConfig passes) {
778    // Important to check for null because if setPassConfig(null) is
779    // called before this.passes is set, getPassConfig() will create a
780    // new PassConfig object and use that, which is probably not what
781    // the client wanted since he or she probably meant to use their
782    // own PassConfig object.
783  0 Preconditions.checkNotNull(passes);
784   
785  0 if (this.passes != null) {
786  0 throw new IllegalStateException("this.passes has already been assigned");
787    }
788  0 this.passes = passes;
789    }
790   
791    /**
792    * Carry out any special checks or procedures that need to be done before
793    * proceeding with rest of the compilation process.
794    *
795    * @return true, to continue with compilation
796    */
 
797  238 toggle boolean precheck() {
798  238 return true;
799    }
800   
 
801  236 toggle public void check() {
802  236 runCustomPasses(CustomPassExecutionTime.BEFORE_CHECKS);
803   
804    // We are currently only interested in check-passes for progress reporting
805    // as it is used for IDEs, that's why the maximum progress is set to 1.0.
806  236 PhaseOptimizer phaseOptimizer = new PhaseOptimizer(this, tracker,
807    new PhaseOptimizer.ProgressRange(getProgress(), 1.0));
808  236 if (options.devMode == DevMode.EVERY_PASS) {
809  0 phaseOptimizer.setSanityCheck(sanityCheck);
810    }
811  236 phaseOptimizer.consume(getPassConfig().getChecks());
812  236 phaseOptimizer.process(externsRoot, jsRoot);
813  236 if (hasErrors()) {
814  7 return;
815    }
816   
817    // TODO(nicksantos): clean this up. The flow here is too hard to follow.
818  229 if (options.nameAnonymousFunctionsOnly) {
819  0 return;
820    }
821   
822  229 if (options.removeTryCatchFinally) {
823  0 removeTryCatchFinally();
824    }
825   
826  229 if (options.getTweakProcessing().shouldStrip() ||
827    !options.stripTypes.isEmpty() ||
828    !options.stripNameSuffixes.isEmpty() ||
829    !options.stripTypePrefixes.isEmpty() ||
830    !options.stripNamePrefixes.isEmpty()) {
831  0 stripCode(options.stripTypes, options.stripNameSuffixes,
832    options.stripTypePrefixes, options.stripNamePrefixes);
833    }
834   
835  229 runCustomPasses(CustomPassExecutionTime.BEFORE_OPTIMIZATIONS);
836    }
837   
 
838  10 toggle private void externExports() {
839  10 logger.fine("Creating extern file for exports");
840  10 startPass("externExports");
841   
842  10 ExternExportsPass pass = new ExternExportsPass(this);
843  10 process(pass);
844   
845  10 externExports = pass.getGeneratedExterns();
846   
847  10 endPass();
848    }
849   
 
850  189 toggle @Override
851    void process(CompilerPass p) {
852  189 p.process(externsRoot, jsRoot);
853    }
854   
855    private final PassFactory sanityCheck =
856    new PassFactory("sanityCheck", false) {
 
857  0 toggle @Override
858    protected CompilerPass create(AbstractCompiler compiler) {
859  0 return new SanityCheck(compiler);
860    }
861    };
862   
 
863  187 toggle private void maybeSanityCheck() {
864  187 if (options.devMode == DevMode.EVERY_PASS) {
865  0 runSanityCheck();
866    }
867    }
868   
 
869  0 toggle private void runSanityCheck() {
870  0 sanityCheck.create(this).process(externsRoot, jsRoot);
871    }
872   
873    /**
874    * Removes try/catch/finally statements for easier debugging.
875    */
 
876  0 toggle void removeTryCatchFinally() {
877  0 logger.fine("Remove try/catch/finally");
878  0 startPass("removeTryCatchFinally");
879  0 RemoveTryCatch r = new RemoveTryCatch(this);
880  0 process(r);
881  0 endPass();
882    }
883   
884    /**
885    * Strips code for smaller compiled code. This is useful for removing debug
886    * statements to prevent leaking them publicly.
887    */
 
888  0 toggle void stripCode(Set<String> stripTypes, Set<String> stripNameSuffixes,
889    Set<String> stripTypePrefixes, Set<String> stripNamePrefixes) {
890  0 logger.fine("Strip code");
891  0 startPass("stripCode");
892  0 StripCode r = new StripCode(this, stripTypes, stripNameSuffixes,
893    stripTypePrefixes, stripNamePrefixes);
894  0 if (options.getTweakProcessing().shouldStrip()) {
895  0 r.enableTweakStripping();
896    }
897  0 process(r);
898  0 endPass();
899    }
900   
901    /**
902    * Runs custom passes that are designated to run at a particular time.
903    */
 
904  465 toggle private void runCustomPasses(CustomPassExecutionTime executionTime) {
905  465 if (options.customPasses != null) {
906  2 Tracer t = newTracer("runCustomPasses");
907  2 try {
908  2 for (CompilerPass p : options.customPasses.get(executionTime)) {
909  1 process(p);
910    }
911    } finally {
912  2 stopTracer(t, "runCustomPasses");
913    }
914    }
915    }
916   
917    private Tracer currentTracer = null;
918    private String currentPassName = null;
919   
920    /**
921    * Marks the beginning of a pass.
922    */
 
923  187 toggle void startPass(String passName) {
924  187 Preconditions.checkState(currentTracer == null);
925  187 currentPassName = passName;
926  187 currentTracer = newTracer(passName);
927    }
928   
929    /**
930    * Marks the end of a pass.
931    */
 
932  187 toggle void endPass() {
933  187 Preconditions.checkState(currentTracer != null,
934    "Tracer should not be null at the end of a pass.");
935  187 stopTracer(currentTracer, currentPassName);
936  187 String passToCheck = currentPassName;
937  187 currentPassName = null;
938  187 currentTracer = null;
939   
940  187 maybeSanityCheck();
941    }
942   
943    /**
944    * Returns a new tracer for the given pass name.
945    */
 
946  18818 toggle Tracer newTracer(String passName) {
947  18818 String comment = passName
948  18818 + (recentChange.hasCodeChanged() ? " on recently changed AST" : "");
949  18818 if (options.tracer.isOn()) {
950  0 tracker.recordPassStart(passName);
951    }
952  18818 return new Tracer("Compiler", comment);
953    }
954   
 
955  18818 toggle void stopTracer(Tracer t, String passName) {
956  18818 long result = t.stop();
957  18818 if (options.tracer.isOn()) {
958  0 tracker.recordPassStop(passName, result);
959    }
960    }
961   
962    /**
963    * Returns the result of the compilation.
964    */
 
965  247 toggle public Result getResult() {
966  247 PassConfig.State state = getPassConfig().getIntermediateState();
967  247 return new Result(getErrors(), getWarnings(), debugLog.toString(),
968    state.variableMap, state.propertyMap,
969    state.anonymousFunctionNameMap, state.stringMap, functionInformationMap,
970    sourceMap, externExports, state.cssNames, state.idGeneratorMap);
971    }
972   
973    /**
974    * Returns an array constructed from errors + temporary warnings.
975    */
 
976  0 toggle public JSError[] getMessages() {
977  0 return getErrors();
978    }
979   
980    /**
981    * Returns the array of errors (never null).
982    */
 
983  24276 toggle public JSError[] getErrors() {
984  24276 return errorManager.getErrors();
985    }
986   
987    /**
988    * Returns the array of warnings (never null).
989    */
 
990  8642 toggle public JSError[] getWarnings() {
991  8642 return errorManager.getWarnings();
992    }
993   
 
994  235 toggle @Override
995    public Node getRoot() {
996  235 return externAndJsRoot;
997    }
998   
999    /**
1000    * Creates a new id for making unique names.
1001    */
 
1002  1213 toggle private int nextUniqueNameId() {
1003  1213 return uniqueNameId++;
1004    }
1005   
1006    /**
1007    * Resets the unique name id counter
1008    */
 
1009  199 toggle @VisibleForTesting
1010    void resetUniqueNameId() {
1011  199 uniqueNameId = 0;
1012    }
1013   
 
1014  631 toggle @Override
1015    Supplier<String> getUniqueNameIdSupplier() {
1016  631 final Compiler self = this;
1017  631 return new Supplier<String>() {
 
1018  1213 toggle @Override
1019    public String get() {
1020  1213 return String.valueOf(self.nextUniqueNameId());
1021    }
1022    };
1023    }
1024   
 
1025  151 toggle @Override
1026    boolean areNodesEqualForInlining(Node n1, Node n2) {
1027  151 if (options.ambiguateProperties ||
1028    options.disambiguateProperties) {
1029    // The type based optimizations require that type information is preserved
1030    // during other optimizations.
1031  2 return n1.isEquivalentToTyped(n2);
1032    } else {
1033  149 return n1.isEquivalentTo(n2);
1034    }
1035    }
1036   
1037    //------------------------------------------------------------------------
1038    // Inputs
1039    //------------------------------------------------------------------------
1040   
1041    // TODO(nicksantos): Decide which parts of these belong in an AbstractCompiler
1042    // interface, and which ones should always be injected.
1043   
 
1044  122363 toggle @Override
1045    public CompilerInput getInput(InputId id) {
1046  122363 return inputsById.get(id);
1047    }
1048   
1049    /**
1050    * Removes an input file from AST.
1051    * @param id The id of the input to be removed.
1052    */
 
1053  0 toggle protected void removeExternInput(InputId id) {
1054  0 CompilerInput input = getInput(id);
1055  0 if (input == null) {
1056  0 return;
1057    }
1058  0 Preconditions.checkState(input.isExtern(), "Not an extern input: %s", input.getName());
1059  0 inputsById.remove(id);
1060  0 externs.remove(input);
1061  0 Node root = input.getAstRoot(this);
1062  0 if (root != null) {
1063  0 root.detachFromParent();
1064    }
1065    }
1066   
 
1067  59 toggle @Override
1068    public CompilerInput newExternInput(String name) {
1069  59 SourceAst ast = new SyntheticAst(name);
1070  59 if (inputsById.containsKey(ast.getInputId())) {
1071  0 throw new IllegalArgumentException("Conflicting externs name: " + name);
1072    }
1073  59 CompilerInput input = new CompilerInput(ast, true);
1074  59 putCompilerInput(input.getInputId(), input);
1075  59 externsRoot.addChildToFront(ast.getAstRoot(this));
1076  59 externs.add(0, input);
1077  59 return input;
1078    }
1079   
 
1080  29736 toggle private CompilerInput putCompilerInput(InputId id, CompilerInput input) {
1081  29736 input.setCompiler(this);
1082  29736 return inputsById.put(id, input);
1083    }
1084   
1085    /** Add a source input dynamically. Intended for incremental compilation. */
 
1086  0 toggle void addIncrementalSourceAst(JsAst ast) {
1087  0 InputId id = ast.getInputId();
1088  0 Preconditions.checkState(getInput(id) == null, "Duplicate input %s", id.getIdName());
1089  0 putCompilerInput(id, new CompilerInput(ast));
1090    }
1091   
1092    /**
1093    * Replace a source input dynamically. Intended for incremental
1094    * re-compilation.
1095    *
1096    * If the new source input doesn't parse, then keep the old input
1097    * in the AST and return false.
1098    *
1099    * @return Whether the new AST was attached successfully.
1100    */
 
1101  0 toggle boolean replaceIncrementalSourceAst(JsAst ast) {
1102  0 CompilerInput oldInput = getInput(ast.getInputId());
1103  0 Preconditions.checkNotNull(oldInput, "No input to replace: %s", ast.getInputId().getIdName());
1104  0 Node newRoot = ast.getAstRoot(this);
1105  0 if (newRoot == null) {
1106  0 return false;
1107    }
1108   
1109  0 Node oldRoot = oldInput.getAstRoot(this);
1110  0 if (oldRoot != null) {
1111  0 oldRoot.getParent().replaceChild(oldRoot, newRoot);
1112    } else {
1113  0 getRoot().getLastChild().addChildToBack(newRoot);
1114    }
1115   
1116  0 CompilerInput newInput = new CompilerInput(ast);
1117  0 putCompilerInput(ast.getInputId(), newInput);
1118   
1119  0 JSModule module = oldInput.getModule();
1120  0 if (module != null) {
1121  0 module.addAfter(newInput, oldInput);
1122  0 module.remove(oldInput);
1123    }
1124   
1125    // Verify the input id is set properly.
1126  0 Preconditions.checkState(
1127    newInput.getInputId().equals(oldInput.getInputId()));
1128  0 InputId inputIdOnAst = newInput.getAstRoot(this).getInputId();
1129  0 Preconditions.checkState(newInput.getInputId().equals(inputIdOnAst));
1130   
1131  0 inputs.remove(oldInput);
1132  0 return true;
1133    }
1134   
1135    /**
1136    * Add a new source input dynamically. Intended for incremental compilation.
1137    * <p>
1138    * If the new source input doesn't parse, it will not be added, and a false
1139    * will be returned.
1140    *
1141    * @param ast the JS Source to add.
1142    * @return true if the source was added successfully, false otherwise.
1143    * @throws IllegalStateException if an input for this ast already exists.
1144    */
 
1145  0 toggle boolean addNewSourceAst(JsAst ast) {
1146  0 CompilerInput oldInput = getInput(ast.getInputId());
1147  0 if (oldInput != null) {
1148  0 throw new IllegalStateException(
1149    "Input already exists: " + ast.getInputId().getIdName());
1150    }
1151  0 Node newRoot = ast.getAstRoot(this);
1152  0 if (newRoot == null) {
1153  0 return false;
1154    }
1155   
1156  0 getRoot().getLastChild().addChildToBack(newRoot);
1157   
1158  0 CompilerInput newInput = new CompilerInput(ast);
1159   
1160    // TODO(tylerg): handle this for multiple modules at some point.
1161  0 if (moduleGraph == null && !modules.isEmpty()) {
1162    // singleton module
1163  0 modules.get(0).add(newInput);
1164    }
1165   
1166  0 putCompilerInput(ast.getInputId(), newInput);
1167   
1168  0 return true;
1169    }
1170   
 
1171  955 toggle @Override
1172    JSModuleGraph getModuleGraph() {
1173  955 return moduleGraph;
1174    }
1175   
1176    /**
1177    * Gets a module graph. This will always return a module graph, even
1178    * in the degenerate case when there's only one module.
1179    */
 
1180  3 toggle JSModuleGraph getDegenerateModuleGraph() {
1181  3 return moduleGraph == null ? new JSModuleGraph(modules) : moduleGraph;
1182    }
1183   
 
1184  22630 toggle @Override
1185    public JSTypeRegistry getTypeRegistry() {
1186  22630 if (typeRegistry == null) {
1187  4587 typeRegistry = new JSTypeRegistry(oldErrorReporter, options.looseTypes);
1188    }
1189  22630 return typeRegistry;
1190    }
1191   
 
1192  26 toggle @Override
1193    public MemoizedScopeCreator getTypedScopeCreator() {
1194  26 return getPassConfig().getTypedScopeCreator();
1195    }
1196   
 
1197  46 toggle @SuppressWarnings("unchecked")
1198    DefaultPassConfig ensureDefaultPassConfig() {
1199  46 PassConfig passes = getPassConfig().getBasePassConfig();
1200  46 Preconditions.checkState(passes instanceof DefaultPassConfig,
1201    "PassConfigs must eventually delegate to the DefaultPassConfig");
1202  46 return (DefaultPassConfig) passes;
1203    }
1204   
 
1205  23 toggle public SymbolTable buildKnownSymbolTable() {
1206  23 SymbolTable symbolTable = new SymbolTable(getTypeRegistry());
1207   
1208  23 MemoizedScopeCreator typedScopeCreator = getTypedScopeCreator();
1209  23 if (typedScopeCreator != null) {
1210  22 symbolTable.addScopes(typedScopeCreator.getAllMemoizedScopes());
1211  22 symbolTable.addSymbolsFrom(typedScopeCreator);
1212    } else {
1213  1 symbolTable.findScopes(this, externsRoot, jsRoot);
1214    }
1215   
1216  23 GlobalNamespace globalNamespace =
1217    ensureDefaultPassConfig().getGlobalNamespace();
1218  23 if (globalNamespace != null) {
1219  22 symbolTable.addSymbolsFrom(globalNamespace);
1220    }
1221   
1222  23 ReferenceCollectingCallback refCollector =
1223    new ReferenceCollectingCallback(
1224    this, ReferenceCollectingCallback.DO_NOTHING_BEHAVIOR);
1225  23 NodeTraversal.traverse(this, getRoot(), refCollector);
1226  23 symbolTable.addSymbolsFrom(refCollector);
1227   
1228  23 PreprocessorSymbolTable preprocessorSymbolTable =
1229    ensureDefaultPassConfig().getPreprocessorSymbolTable();
1230  23 if (preprocessorSymbolTable != null) {
1231  22 symbolTable.addSymbolsFrom(preprocessorSymbolTable);
1232    }
1233   
1234  23 symbolTable.fillNamespaceReferences();
1235  23 symbolTable.fillPropertyScopes();
1236  23 symbolTable.fillThisReferences(this, externsRoot, jsRoot);
1237  23 symbolTable.fillPropertySymbols(this, externsRoot, jsRoot);
1238  23 symbolTable.fillJSDocInfo(this, externsRoot, jsRoot);
1239   
1240  23 return symbolTable;
1241    }
1242   
 
1243  3 toggle @Override
1244    public Scope getTopScope() {
1245  3 return getPassConfig().getTopScope();
1246    }
1247   
 
1248  210 toggle @Override
1249    public ReverseAbstractInterpreter getReverseAbstractInterpreter() {
1250  210 if (abstractInterpreter == null) {
1251  147 ChainableReverseAbstractInterpreter interpreter =
1252    new SemanticReverseAbstractInterpreter(
1253    getCodingConvention(), getTypeRegistry());
1254  147 if (options.closurePass) {
1255  72 interpreter = new ClosureReverseAbstractInterpreter(
1256    getCodingConvention(), getTypeRegistry())
1257    .append(interpreter).getFirst();
1258    }
1259  147 abstractInterpreter = interpreter;
1260    }
1261  210 return abstractInterpreter;
1262    }
1263   
 
1264  1313 toggle @Override
1265    TypeValidator getTypeValidator() {
1266  1313 if (typeValidator == null) {
1267  613 typeValidator = new TypeValidator(this);
1268    }
1269  1313 return typeValidator;
1270    }
1271   
1272    //------------------------------------------------------------------------
1273    // Parsing
1274    //------------------------------------------------------------------------
1275   
1276    /**
1277    * Parses the externs and main inputs.
1278    *
1279    * @return A synthetic root node whose two children are the externs root
1280    * and the main root
1281    */
 
1282  18308 toggle Node parseInputs() {
1283  18308 boolean devMode = options.devMode != DevMode.OFF;
1284   
1285    // If old roots exist (we are parsing a second time), detach each of the
1286    // individual file parse trees.
1287  18308 if (externsRoot != null) {
1288  0 externsRoot.detachChildren();
1289    }
1290  18308 if (jsRoot != null) {
1291  0 jsRoot.detachChildren();
1292    }
1293   
1294    // Parse main JS sources.
1295  18308 jsRoot = IR.block();
1296  18308 jsRoot.setIsSyntheticBlock(true);
1297   
1298  18308 externsRoot = IR.block();
1299  18308 externsRoot.setIsSyntheticBlock(true);
1300   
1301  18308 externAndJsRoot = IR.block(externsRoot, jsRoot);
1302  18308 externAndJsRoot.setIsSyntheticBlock(true);
1303   
1304  18308 if (options.tracer.isOn()) {
1305  0 tracker = new PerformanceTracker(jsRoot, options.tracer);
1306  0 addChangeHandler(tracker.getCodeChangeHandler());
1307    }
1308   
1309  18308 Tracer tracer = newTracer("parseInputs");
1310   
1311  18308 try {
1312    // Parse externs sources.
1313  18308 for (CompilerInput input : externs) {
1314  8776 Node n = input.getAstRoot(this);
1315  8776 if (hasErrors()) {
1316  0 return null;
1317    }
1318  8776 externsRoot.addChildToBack(n);
1319    }
1320   
1321    // Modules inferred in ProcessCommonJS pass.
1322  18308 if (options.transformAMDToCJSModules || options.processCommonJSModules) {
1323  9 processAMDAndCommonJSModules();
1324    }
1325   
1326  18308 hoistExterns(externsRoot);
1327   
1328    // Check if the sources need to be re-ordered.
1329  18308 boolean staleInputs = false;
1330  18308 if (options.dependencyOptions.needsManagement()) {
1331  113 for (CompilerInput input : inputs) {
1332    // Forward-declare all the provided types, so that they
1333    // are not flagged even if they are dropped from the process.
1334  141 for (String provide : input.getProvides()) {
1335  32 getTypeRegistry().forwardDeclareType(provide);
1336    }
1337    }
1338   
1339  113 try {
1340  113 inputs =
1341  113 (moduleGraph == null ? new JSModuleGraph(modules) : moduleGraph)
1342    .manageDependencies(options.dependencyOptions, inputs);
1343  110 staleInputs = true;
1344    } catch (CircularDependencyException e) {
1345  2 report(JSError.make(
1346    JSModule.CIRCULAR_DEPENDENCY_ERROR, e.getMessage()));
1347   
1348    // If in IDE mode, we ignore the error and keep going.
1349  2 if (hasErrors()) {
1350  1 return null;
1351    }
1352    } catch (MissingProvideException e) {
1353  1 report(JSError.make(
1354    MISSING_ENTRY_ERROR, e.getMessage()));
1355   
1356    // If in IDE mode, we ignore the error and keep going.
1357  1 if (hasErrors()) {
1358  1 return null;
1359    }
1360    }
1361    }
1362   
1363  18306 hoistNoCompileFiles();
1364   
1365  18306 if (staleInputs) {
1366  110 repartitionInputs();
1367    }
1368   
1369    // Build the AST.
1370  18306 for (CompilerInput input : inputs) {
1371  18519 Node n = input.getAstRoot(this);
1372  18519 if (n == null) {
1373  1 continue;
1374    }
1375   
1376  18518 if (devMode) {
1377  0 runSanityCheck();
1378  0 if (hasErrors()) {
1379  0 return null;
1380    }
1381    }
1382   
1383  18518 if (options.sourceMapOutputPath != null ||
1384    options.nameReferenceReportPath != null) {
1385   
1386    // Annotate the nodes in the tree with information from the
1387    // input file. This information is used to construct the SourceMap.
1388  27 SourceInformationAnnotator sia =
1389    new SourceInformationAnnotator(
1390    input.getName(), options.devMode != DevMode.OFF);
1391  27 NodeTraversal.traverse(this, n, sia);
1392    }
1393   
1394  18518 jsRoot.addChildToBack(n);
1395    }
1396   
1397  18306 if (hasErrors()) {
1398  7 return null;
1399    }
1400  18299 return externAndJsRoot;
1401    } finally {
1402  18308 stopTracer(tracer, "parseInputs");
1403    }
1404    }
1405   
1406    /**
1407    * Hoists inputs with the @externs annotation into the externs list.
1408    */
 
1409  18308 toggle private void hoistExterns(Node externsRoot) {
1410  18308 boolean staleInputs = false;
1411  18308 for (CompilerInput input : inputs) {
1412  18525 if (options.dependencyOptions.needsManagement()) {
1413    // If we're doing scanning dependency info anyway, use that
1414    // information to skip sources that obviously aren't externs.
1415  141 if (!input.getProvides().isEmpty() || !input.getRequires().isEmpty()) {
1416  38 continue;
1417    }
1418    }
1419   
1420  18487 Node n = input.getAstRoot(this);
1421   
1422    // Inputs can have a null AST on a parse error.
1423  18487 if (n == null) {
1424  1 continue;
1425    }
1426   
1427  18486 JSDocInfo info = n.getJSDocInfo();
1428  18486 if (info != null && info.isExterns()) {
1429    // If the input file is explicitly marked as an externs file, then
1430    // assume the programmer made a mistake and throw it into
1431    // the externs pile anyways.
1432  1 externsRoot.addChildToBack(n);
1433  1 input.setIsExtern(true);
1434   
1435  1 input.getModule().remove(input);
1436   
1437  1 externs.add(input);
1438  1 staleInputs = true;
1439    }
1440    }
1441   
1442  18308 if (staleInputs) {
1443  1 repartitionInputs();
1444    }
1445    }
1446   
1447    /**
1448    * Hoists inputs with the @nocompiler annotation out of the inputs.
1449    */
 
1450  18306 toggle private void hoistNoCompileFiles() {
1451  18306 boolean staleInputs = false;
1452  18306 for (CompilerInput input : inputs) {
1453  18513 Node n = input.getAstRoot(this);
1454   
1455    // Inputs can have a null AST on a parse error.
1456  18513 if (n == null) {
1457  1 continue;
1458    }
1459   
1460  18512 JSDocInfo info = n.getJSDocInfo();
1461  18512 if (info != null && info.isNoCompile()) {
1462  1 input.getModule().remove(input);
1463  1 staleInputs = true;
1464    }
1465    }
1466   
1467  18306 if (staleInputs) {
1468  1 repartitionInputs();
1469    }
1470    }
1471   
 
1472  112 toggle private void repartitionInputs() {
1473  112 fillEmptyModules(modules);
1474  112 rebuildInputsFromModules();
1475    }
1476   
1477    /**
1478    * Transforms AMD and CJS modules to something closure compiler can
1479    * process and creates JSModules and the corresponding dependency tree
1480    * on the way.
1481    */
 
1482  10 toggle void processAMDAndCommonJSModules() {
1483  10 Map<String, JSModule> modulesByName = Maps.newLinkedHashMap();
1484  10 Map<CompilerInput, JSModule> modulesByInput = Maps.newLinkedHashMap();
1485    // TODO(nicksantos): Refactor module dependency resolution to work nicely
1486    // with multiple ways to express dependencies. Directly support JSModules
1487    // that are equivalent to a signal file and which express their deps
1488    // directly in the source.
1489  10 for (CompilerInput input : inputs) {
1490  25 input.setCompiler(this);
1491  25 Node root = input.getAstRoot(this);
1492  25 if (root == null) {
1493  0 continue;
1494    }
1495  25 if (options.transformAMDToCJSModules) {
1496  1 new TransformAMDToCJSModule(this).process(null, root);
1497    }
1498  25 if (options.processCommonJSModules) {
1499  25 ProcessCommonJSModules cjs = new ProcessCommonJSModules(this,
1500    options.commonJSModulePathPrefix);
1501  25 cjs.process(null, root);
1502  25 JSModule m = cjs.getModule();
1503  25 if (m != null) {
1504  25 modulesByName.put(m.getName(), m);
1505  25 modulesByInput.put(input, m);
1506    }
1507    }
1508    }
1509  10 if (options.processCommonJSModules) {
1510  10 List<JSModule> modules = Lists.newArrayList(modulesByName.values());
1511  10 if (!modules.isEmpty()) {
1512  10 this.modules = modules;
1513  10 this.moduleGraph = new JSModuleGraph(this.modules);
1514    }
1515  10 for (JSModule module : modules) {
1516  25 for (CompilerInput input : module.getInputs()) {
1517  25 for (String require : input.getRequires()) {
1518  21 JSModule dependency = modulesByName.get(require);
1519  21 if (dependency == null) {
1520  2 report(JSError.make(MISSING_ENTRY_ERROR, require));
1521    } else {
1522  19 module.addDependency(dependency);
1523    }
1524    }
1525    }
1526    }
1527  10 try {
1528  10 modules = Lists.newArrayList();
1529  10 for (CompilerInput input : this.moduleGraph.manageDependencies(
1530    options.dependencyOptions, inputs)) {
1531  24 modules.add(modulesByInput.get(input));
1532    }
1533  10 JSModule root = new JSModule("root");
1534  10 for (JSModule m : modules) {
1535  24 m.addDependency(root);
1536    }
1537  10 modules.add(0, root);
1538  10 SortedDependencies<JSModule> sorter =
1539    new SortedDependencies<JSModule>(modules);
1540  10 modules = sorter.getDependenciesOf(modules, true);
1541  10 this.modules = modules;
1542   
1543  10 this.moduleGraph = new JSModuleGraph(modules);
1544    } catch (Exception e) {
1545  0 Throwables.propagate(e);
1546    }
1547    }
1548    }
1549   
 
1550  47 toggle public Node parse(SourceFile file) {
1551  47 initCompilerOptionsIfTesting();
1552  47 addToDebugLog("Parsing: " + file.getName());
1553  47 return new JsAst(file).getAstRoot(this);
1554    }
1555   
1556    private int syntheticCodeId = 0;
1557   
 
1558  56 toggle @Override
1559    Node parseSyntheticCode(String js) {
1560  56 CompilerInput input = new CompilerInput(
1561    SourceFile.fromCode(" [synthetic:" + (++syntheticCodeId) + "] ", js));
1562  56 putCompilerInput(input.getInputId(), input);
1563  56 return input.getAstRoot(this);
1564    }
1565   
1566    /**
1567    * Allow subclasses to override the default CompileOptions object.
1568    */
 
1569  910 toggle protected CompilerOptions newCompilerOptions() {
1570  910 return new CompilerOptions();
1571    }
1572   
 
1573  29461 toggle void initCompilerOptionsIfTesting() {
1574  29461 if (options == null) {
1575    // initialization for tests that don't initialize the compiler
1576    // by the normal mechanisms.
1577  907 initOptions(newCompilerOptions());
1578    }
1579    }
1580   
 
1581  47 toggle @Override
1582    Node parseSyntheticCode(String fileName, String js) {
1583  47 initCompilerOptionsIfTesting();
1584  47 return parse(SourceFile.fromCode(fileName, js));
1585    }
1586   
 
1587  1575 toggle @Override
1588    Node parseTestCode(String js) {
1589  1575 initCompilerOptionsIfTesting();
1590  1575 CompilerInput input = new CompilerInput(
1591    SourceFile.fromCode("[testcode]", js));
1592  1575 if (inputsById == null) {
1593  1501 inputsById = Maps.newHashMap();
1594    }
1595  1575 putCompilerInput(input.getInputId(), input);
1596  1575 return input.getAstRoot(this);
1597    }
1598   
 
1599  29447 toggle @Override
1600    ErrorReporter getDefaultErrorReporter() {
1601  29447 return defaultErrorReporter;
1602    }
1603   
1604    //------------------------------------------------------------------------
1605    // Convert back to source code
1606    //------------------------------------------------------------------------
1607   
1608    /**
1609    * Converts the main parse tree back to JS code.
1610    */
 
1611  76 toggle public String toSource() {
1612  76 return runInCompilerThread(new Callable<String>() {
 
1613  76 toggle @Override
1614    public String call() throws Exception {
1615  76 Tracer tracer = newTracer("toSource");
1616  76 try {
1617  76 CodeBuilder cb = new CodeBuilder();
1618  76 if (jsRoot != null) {
1619  76 int i = 0;
1620  76 for (Node scriptNode = jsRoot.getFirstChild();
1621  168 scriptNode != null;
1622    scriptNode = scriptNode.getNext()) {
1623  92 toSource(cb, i++, scriptNode);
1624    }
1625    }
1626  76 return cb.toString();
1627    } finally {
1628  76 stopTracer(tracer, "toSource");
1629    }
1630    }
1631    });
1632    }
1633   
1634    /**
1635    * Converts the parse tree for each input back to JS code.
1636    */
 
1637  0 toggle public String[] toSourceArray() {
1638  0 return runInCompilerThread(new Callable<String[]>() {
 
1639  0 toggle @Override
1640    public String[] call() throws Exception {
1641  0 Tracer tracer = newTracer("toSourceArray");
1642  0 try {
1643  0 int numInputs = inputs.size();
1644  0 String[] sources = new String[numInputs];
1645  0 CodeBuilder cb = new CodeBuilder();
1646  0 for (int i = 0; i < numInputs; i++) {
1647  0 Node scriptNode = inputs.get(i).getAstRoot(Compiler.this);
1648  0 cb.reset();
1649  0 toSource(cb, i, scriptNode);
1650  0 sources[i] = cb.toString();
1651    }
1652  0 return sources;
1653    } finally {
1654  0 stopTracer(tracer, "toSourceArray");
1655    }
1656    }
1657    });
1658    }
1659   
1660    /**
1661    * Converts the parse tree for a module back to JS code.
1662    */
 
1663  15 toggle public String toSource(final JSModule module) {
1664  15 return runInCompilerThread(new Callable<String>() {
 
1665  15 toggle @Override
1666    public String call() throws Exception {
1667  15 List<CompilerInput> inputs = module.getInputs();
1668  15 int numInputs = inputs.size();
1669  15 if (numInputs == 0) {
1670  0 return "";
1671    }
1672  15 CodeBuilder cb = new CodeBuilder();
1673  30 for (int i = 0; i < numInputs; i++) {
1674  15 Node scriptNode = inputs.get(i).getAstRoot(Compiler.this);
1675  15 if (scriptNode == null) {
1676  0 throw new IllegalArgumentException(
1677    "Bad module: " + module.getName());
1678    }
1679  15 toSource(cb, i, scriptNode);
1680    }
1681  15 return cb.toString();
1682    }
1683    });
1684    }
1685   
1686   
1687    /**
1688    * Converts the parse tree for each input in a module back to JS code.
1689    */
 
1690  0 toggle public String[] toSourceArray(final JSModule module) {
1691  0 return runInCompilerThread(new Callable<String[]>() {
 
1692  0 toggle @Override
1693    public String[] call() throws Exception {
1694  0 List<CompilerInput> inputs = module.getInputs();
1695  0 int numInputs = inputs.size();
1696  0 if (numInputs == 0) {
1697  0 return new String[0];
1698    }
1699   
1700  0 String[] sources = new String[numInputs];
1701  0 CodeBuilder cb = new CodeBuilder();
1702  0 for (int i = 0; i < numInputs; i++) {
1703  0 Node scriptNode = inputs.get(i).getAstRoot(Compiler.this);
1704  0 if (scriptNode == null) {
1705  0 throw new IllegalArgumentException(
1706    "Bad module input: " + inputs.get(i).getName());
1707    }
1708   
1709  0 cb.reset();
1710  0 toSource(cb, i, scriptNode);
1711  0 sources[i] = cb.toString();
1712    }
1713  0 return sources;
1714    }
1715    });
1716    }
1717   
1718    /**
1719    * Writes out JS code from a root node. If printing input delimiters, this
1720    * method will attach a comment to the start of the text indicating which
1721    * input the output derived from. If there were any preserve annotations
1722    * within the root's source, they will also be printed in a block comment
1723    * at the beginning of the output.
1724    */
 
1725  107 toggle public void toSource(final CodeBuilder cb,
1726    final int inputSeqNum,
1727    final Node root) {
1728  107 runInCompilerThread(new Callable<Void>() {
 
1729  107 toggle @Override
1730    public Void call() throws Exception {
1731  107 if (options.printInputDelimiter) {
1732  0 if ((cb.getLength() > 0) && !cb.endsWith("\n")) {
1733  0 cb.append("\n"); // Make sure that the label starts on a new line
1734    }
1735  0 Preconditions.checkState(root.isScript());
1736   
1737  0 String delimiter = options.inputDelimiter;
1738   
1739  0 String inputName = root.getInputId().getIdName();
1740  0 String sourceName = root.getSourceFileName();
1741  0 Preconditions.checkState(sourceName != null);
1742  0 Preconditions.checkState(!sourceName.isEmpty());
1743   
1744  0 delimiter = delimiter
1745    .replaceAll("%name%", Matcher.quoteReplacement(inputName))
1746    .replaceAll("%num%", String.valueOf(inputSeqNum));
1747   
1748  0 cb.append(delimiter)
1749    .append("\n");
1750    }
1751  107 if (root.getJSDocInfo() != null &&
1752    root.getJSDocInfo().getLicense() != null) {
1753  6 cb.append("/*\n")
1754    .append(root.getJSDocInfo().getLicense())
1755    .append("*/\n");
1756    }
1757   
1758    // If there is a valid source map, then indicate to it that the current
1759    // root node's mappings are offset by the given string builder buffer.
1760  107 if (options.sourceMapOutputPath != null) {
1761  27 sourceMap.setStartingPosition(
1762    cb.getLineIndex(), cb.getColumnIndex());
1763    }
1764   
1765    // if LanguageMode is ECMASCRIPT5_STRICT, only print 'use strict'
1766    // for the first input file
1767  107 String code = toSource(root, sourceMap, inputSeqNum == 0);
1768  107 if (!code.isEmpty()) {
1769  92 cb.append(code);
1770   
1771    // In order to avoid parse ambiguity when files are concatenated
1772    // together, all files should end in a semi-colon. Do a quick
1773    // heuristic check if there's an obvious semi-colon already there.
1774  92 int length = code.length();
1775  92 char lastChar = code.charAt(length - 1);
1776  92 char secondLastChar = length >= 2 ?
1777    code.charAt(length - 2) : '\0';
1778  92 boolean hasSemiColon = lastChar == ';' ||
1779    (lastChar == '\n' && secondLastChar == ';');
1780  92 if (!hasSemiColon) {
1781  90 cb.append(";");
1782    }
1783    }
1784  107 return null;
1785    }
1786    });
1787    }
1788   
1789    /**
1790    * Generates JavaScript source code for an AST, doesn't generate source
1791    * map info.
1792    */
 
1793  27165 toggle @Override
1794    String toSource(Node n) {
1795  27165 initCompilerOptionsIfTesting();
1796  27165 return toSource(n, null, true);
1797    }
1798   
1799    /**
1800    * Generates JavaScript source code for an AST.
1801    */
 
1802  27272 toggle private String toSource(Node n, SourceMap sourceMap, boolean firstOutput) {
1803  27272 CodePrinter.Builder builder = new CodePrinter.Builder(n);
1804  27272 builder.setCompilerOptions(options);
1805  27272 builder.setSourceMap(sourceMap);
1806  27272 builder.setTagAsStrict(firstOutput &&
1807    options.getLanguageOut() == LanguageMode.ECMASCRIPT5_STRICT);
1808  27272 return builder.build();
1809    }
1810   
1811    /**
1812    * Stores a buffer of text to which more can be appended. This is just like a
1813    * StringBuilder except that we also track the number of lines.
1814    */
 
1815    public static class CodeBuilder {
1816    private final StringBuilder sb = new StringBuilder();
1817    private int lineCount = 0;
1818    private int colCount = 0;
1819   
1820    /** Removes all text, but leaves the line count unchanged. */
 
1821  1 toggle void reset() {
1822  1 sb.setLength(0);
1823    }
1824   
1825    /** Appends the given string to the text buffer. */
 
1826  204 toggle CodeBuilder append(String str) {
1827  204 sb.append(str);
1828   
1829    // Adjust the line and column information for the new text.
1830  204 int index = -1;
1831  204 int lastIndex = index;
1832  ? while ((index = str.indexOf('\n', index + 1)) >= 0) {
1833  32 ++lineCount;
1834  32 lastIndex = index;
1835    }
1836   
1837  204 if (lastIndex == -1) {
1838    // No new lines, append the new characters added.
1839  178 colCount += str.length();
1840    } else {
1841  26 colCount = str.length() - (lastIndex + 1);
1842    }
1843   
1844  204 return this;
1845    }
1846   
1847    /** Returns all text in the text buffer. */
 
1848  93 toggle @Override
1849    public String toString() {
1850  93 return sb.toString();
1851    }
1852   
1853    /** Returns the length of the text buffer. */
 
1854  0 toggle public int getLength() {
1855  0 return sb.length();
1856    }
1857   
1858    /** Returns the (zero-based) index of the last line in the text buffer. */
 
1859  32 toggle int getLineIndex() {
1860  32 return lineCount;
1861    }
1862   
1863    /** Returns the (zero-based) index of the last column in the text buffer. */
 
1864  32 toggle int getColumnIndex() {
1865  32 return colCount;
1866    }
1867   
1868    /** Determines whether the text ends with the given suffix. */
 
1869  0 toggle boolean endsWith(String suffix) {
1870  0 return (sb.length() > suffix.length())
1871    && suffix.equals(sb.substring(sb.length() - suffix.length()));
1872    }
1873    }
1874   
1875    //------------------------------------------------------------------------
1876    // Optimizations
1877    //------------------------------------------------------------------------
1878   
 
1879  176 toggle public void optimize() {
1880    // Ideally, this pass should be the first pass run, however:
1881    // 1) VariableReferenceCheck reports unexpected warnings if Normalize
1882    // is done first.
1883    // 2) ReplaceMessages, stripCode, and potentially custom passes rely on
1884    // unmodified local names.
1885  176 normalize();
1886   
1887  176 PhaseOptimizer phaseOptimizer = new PhaseOptimizer(this, tracker, null);
1888  176 if (options.devMode == DevMode.EVERY_PASS) {
1889  0 phaseOptimizer.setSanityCheck(sanityCheck);
1890    }
1891  176 phaseOptimizer.consume(getPassConfig().getOptimizations());
1892  176 phaseOptimizer.process(externsRoot, jsRoot);
1893    }
1894   
 
1895  5 toggle @Override
1896    void setCssRenamingMap(CssRenamingMap map) {
1897  5 options.cssRenamingMap = map;
1898    }
1899   
 
1900  106 toggle @Override
1901    CssRenamingMap getCssRenamingMap() {
1902  106 return options.cssRenamingMap;
1903    }
1904   
1905    /**
1906    * Reprocesses the current defines over the AST. This is used by GwtCompiler
1907    * to generate N outputs for different targets from the same (checked) AST.
1908    * For each target, we apply the target-specific defines by calling
1909    * {@code processDefines} and then {@code optimize} to optimize the AST
1910    * specifically for that target.
1911    */
 
1912  0 toggle public void processDefines() {
1913  0 (new DefaultPassConfig(options)).processDefines.create(this)
1914    .process(externsRoot, jsRoot);
1915    }
1916   
 
1917  0 toggle boolean isInliningForbidden() {
1918  0 return options.propertyRenaming == PropertyRenamingPolicy.HEURISTIC ||
1919    options.propertyRenaming ==
1920    PropertyRenamingPolicy.AGGRESSIVE_HEURISTIC;
1921    }
1922   
1923    /** Control Flow Analysis. */
 
1924  1 toggle ControlFlowGraph<Node> computeCFG() {
1925  1 logger.fine("Computing Control Flow Graph");
1926  1 Tracer tracer = newTracer("computeCFG");
1927  1 ControlFlowAnalysis cfa = new ControlFlowAnalysis(this, true, false);
1928  1 process(cfa);
1929  1 stopTracer(tracer, "computeCFG");
1930  1 return cfa.getCfg();
1931    }
1932   
 
1933  177 toggle public void normalize() {
1934  177 logger.fine("Normalizing");
1935  177 startPass("normalize");
1936  177 process(new Normalize(this, false));
1937  177 endPass();
1938    }
1939   
 
1940  29438 toggle @Override
1941    void prepareAst(Node root) {
1942  29438 CompilerPass pass = new PrepareAst(this);
1943  29438 pass.process(null, root);
1944    }
1945   
 
1946  0 toggle void recordFunctionInformation() {
1947  0 logger.fine("Recording function information");
1948  0 startPass("recordFunctionInformation");
1949  0 RecordFunctionInformation recordFunctionInfoPass =
1950    new RecordFunctionInformation(
1951    this, getPassConfig().getIntermediateState().functionNames);
1952  0 process(recordFunctionInfoPass);
1953  0 functionInformationMap = recordFunctionInfoPass.getMap();
1954  0 endPass();
1955    }
1956   
1957    protected final CodeChangeHandler.RecentChange recentChange =
1958    new CodeChangeHandler.RecentChange();
1959    private final List<CodeChangeHandler> codeChangeHandlers =
1960    Lists.<CodeChangeHandler>newArrayList();
1961   
1962    /** Name of the synthetic input that holds synthesized externs. */
1963    static final String SYNTHETIC_EXTERNS = "{SyntheticVarsDeclar}";
1964   
1965    private CompilerInput synthesizedExternsInput = null;
1966   
 
1967  37952 toggle @Override
1968    void addChangeHandler(CodeChangeHandler handler) {
1969  37952 codeChangeHandlers.add(handler);
1970    }
1971   
 
1972  12918 toggle @Override
1973    void removeChangeHandler(CodeChangeHandler handler) {
1974  12918 codeChangeHandlers.remove(handler);
1975    }
1976   
1977    /**
1978    * All passes should call reportCodeChange() when they alter
1979    * the JS tree structure. This is verified by CompilerTestCase.
1980    * This allows us to optimize to a fixed point.
1981    */
 
1982  18069 toggle @Override
1983    public void reportCodeChange() {
1984  18069 for (CodeChangeHandler handler : codeChangeHandlers) {
1985  36032 handler.reportChange();
1986    }
1987    }
1988   
 
1989  109240 toggle @Override
1990    public CodingConvention getCodingConvention() {
1991  109240 CodingConvention convention = options.getCodingConvention();
1992  109240 convention = convention != null ? convention : defaultCodingConvention;
1993  109240 return convention;
1994    }
1995   
 
1996  85279 toggle @Override
1997    public boolean isIdeMode() {
1998  85279 return options.ideMode;
1999    }
2000   
 
2001  29 toggle @Override
2002    public boolean acceptEcmaScript5() {
2003  29 switch (options.getLanguageIn()) {
2004  21 case ECMASCRIPT5:
2005  0 case ECMASCRIPT5_STRICT:
2006  21 return true;
2007  8 case ECMASCRIPT3:
2008  8 return false;
2009    }
2010  0 throw new IllegalStateException("unexpected language mode");
2011    }
2012   
 
2013  0 toggle public LanguageMode languageMode() {
2014  0 return options.getLanguageIn();
2015    }
2016   
 
2017  20079 toggle @Override
2018    public boolean acceptConstKeyword() {
2019  20079 return options.acceptConstKeyword;
2020    }
2021   
 
2022  29447 toggle @Override
2023    Config getParserConfig() {
2024  29447 if (parserConfig == null) {
2025  20079 Config.LanguageMode mode;
2026  20079 switch (options.getLanguageIn()) {
2027  687 case ECMASCRIPT3:
2028  687 mode = Config.LanguageMode.ECMASCRIPT3;
2029  687 break;
2030  19387 case ECMASCRIPT5:
2031  19387 mode = Config.LanguageMode.ECMASCRIPT5;
2032  19387 break;
2033  5 case ECMASCRIPT5_STRICT:
2034  5 mode = Config.LanguageMode.ECMASCRIPT5_STRICT;
2035  5 break;
2036  0 default:
2037  0 throw new IllegalStateException("unexpected language mode");
2038    }
2039   
2040  20079 parserConfig = ParserRunner.createConfig(
2041    isIdeMode(),
2042    mode,
2043    acceptConstKeyword(),
2044    options.extraAnnotationNames);
2045    }
2046  29447 return parserConfig;
2047    }
2048   
 
2049  0 toggle @Override
2050    public boolean isTypeCheckingEnabled() {
2051  0 return options.checkTypes;
2052    }
2053   
2054   
2055    //------------------------------------------------------------------------
2056    // Error reporting
2057    //------------------------------------------------------------------------
2058   
2059    /**
2060    * The warning classes that are available from the command-line, and
2061    * are suppressible by the {@code @suppress} annotation.
2062    */
 
2063  20462 toggle protected DiagnosticGroups getDiagnosticGroups() {
2064  20462 return new DiagnosticGroups();
2065    }
2066   
 
2067  859 toggle @Override
2068    public void report(JSError error) {
2069  859 CheckLevel level = error.getDefaultLevel();
2070  859 if (warningsGuard != null) {
2071  859 CheckLevel newLevel = warningsGuard.level(error);
2072  859 if (newLevel != null) {
2073  137 level = newLevel;
2074    }
2075    }
2076   
2077  859 if (level.isOn()) {
2078  733 if (getOptions().errorHandler != null) {
2079  0 getOptions().errorHandler.report(level, error);
2080    }
2081  733 errorManager.report(level, error);
2082    }
2083    }
2084   
 
2085  532 toggle @Override
2086    public CheckLevel getErrorLevel(JSError error) {
2087  532 Preconditions.checkNotNull(options);
2088  532 return warningsGuard.level(error);
2089    }
2090   
2091    /**
2092    * Report an internal error.
2093    */
 
2094  6 toggle @Override
2095    void throwInternalError(String message, Exception cause) {
2096  6 String finalMessage =
2097    "INTERNAL COMPILER ERROR.\n" +
2098    "Please report this problem.\n" + message;
2099   
2100  6 RuntimeException e = new RuntimeException(finalMessage, cause);
2101  6 if (cause != null) {
2102  6 e.setStackTrace(cause.getStackTrace());
2103    }
2104  6 throw e;
2105    }
2106   
2107   
2108    /**
2109    * Gets the number of errors.
2110    */
 
2111  76590 toggle public int getErrorCount() {
2112  76590 return errorManager.getErrorCount();
2113    }
2114   
2115    /**
2116    * Gets the number of warnings.
2117    */
 
2118  46 toggle public int getWarningCount() {
2119  46 return errorManager.getWarningCount();
2120    }
2121   
 
2122  65199 toggle @Override
2123    boolean hasHaltingErrors() {
2124  65199 return !isIdeMode() && getErrorCount() > 0;
2125    }
2126   
2127    /**
2128    * Consults the {@link ErrorManager} to see if we've encountered errors
2129    * that should halt compilation. <p>
2130    *
2131    * If {@link CompilerOptions#ideMode} is {@code true}, this function
2132    * always returns {@code false} without consulting the error manager. The
2133    * error manager will continue to be told about new errors and warnings, but
2134    * the compiler will complete compilation of all inputs.<p>
2135    */
 
2136  28728 toggle public boolean hasErrors() {
2137  28728 return hasHaltingErrors();
2138    }
2139   
2140    /** Called from the compiler passes, adds debug info */
 
2141  548 toggle @Override
2142    void addToDebugLog(String str) {
2143  548 debugLog.append(str);
2144  548 debugLog.append('\n');
2145  548 logger.fine(str);
2146    }
2147   
 
2148  167 toggle @Override
2149    SourceFile getSourceFileByName(String sourceName) {
2150    // Here we assume that the source name is the input name, this
2151    // is try of JavaScript parsed from source.
2152  167 if (sourceName != null) {
2153  166 CompilerInput input = inputsById.get(new InputId(sourceName));
2154  166 if (input != null) {
2155  150 return input.getSourceFile();
2156    }
2157    }
2158  17 return null;
2159    }
2160   
 
2161  136 toggle @Override
2162    public String getSourceLine(String sourceName, int lineNumber) {
2163  136 if (lineNumber < 1) {
2164  6 return null;
2165    }
2166  130 SourceFile input = getSourceFileByName(sourceName);
2167  130 if (input != null) {
2168  129 return input.getLine(lineNumber);
2169    }
2170  1 return null;
2171    }
2172   
 
2173  14 toggle @Override
2174    public Region getSourceRegion(String sourceName, int lineNumber) {
2175  14 if (lineNumber < 1) {
2176  3 return null;
2177    }
2178  11 SourceFile input = getSourceFileByName(sourceName);
2179  11 if (input != null) {
2180  9 return input.getRegion(lineNumber);
2181    }
2182  2 return null;
2183    }
2184   
2185    //------------------------------------------------------------------------
2186    // Package-private helpers
2187    //------------------------------------------------------------------------
2188   
 
2189  121 toggle @Override
2190    Node getNodeForCodeInsertion(JSModule module) {
2191  121 if (module == null) {
2192  54 if (inputs.isEmpty()) {
2193  0 throw new IllegalStateException("No inputs");
2194    }
2195   
2196  54 return inputs.get(0).getAstRoot(this);
2197    }
2198   
2199  67 List<CompilerInput> moduleInputs = module.getInputs();
2200  67 if (moduleInputs.size() > 0) {
2201  67 return moduleInputs.get(0).getAstRoot(this);
2202    }
2203  0 throw new IllegalStateException("Root module has no inputs");
2204    }
2205   
 
2206  11 toggle public SourceMap getSourceMap() {
2207  11 return sourceMap;
2208    }
2209   
 
2210  0 toggle VariableMap getVariableMap() {
2211  0 return getPassConfig().getIntermediateState().variableMap;
2212    }
2213   
 
2214  0 toggle VariableMap getPropertyMap() {
2215  0 return getPassConfig().getIntermediateState().propertyMap;
2216    }
2217   
 
2218  1604 toggle CompilerOptions getOptions() {
2219  1604 return options;
2220    }
2221   
 
2222  0 toggle FunctionInformationMap getFunctionalInformationMap() {
2223  0 return functionInformationMap;
2224    }
2225   
2226    /**
2227    * Sets the logging level for the com.google.javascript.jscomp package.
2228    */
 
2229  63 toggle public static void setLoggingLevel(Level level) {
2230  63 logger.setLevel(level);
2231    }
2232   
2233    /** Gets the DOT graph of the AST generated at the end of compilation. */
 
2234  0 toggle public String getAstDotGraph() throws IOException {
2235  0 if (jsRoot != null) {
2236  0 ControlFlowAnalysis cfa = new ControlFlowAnalysis(this, true, false);
2237  0 cfa.process(null, jsRoot);
2238  0 return DotFormatter.toDot(jsRoot, cfa.getCfg());
2239    } else {
2240  0 return "";
2241    }
2242    }
2243   
 
2244  1539 toggle @Override
2245    public ErrorManager getErrorManager() {
2246  1539 if (options == null) {
2247  3 initOptions(newCompilerOptions());
2248    }
2249  1539 return errorManager;
2250    }
2251   
 
2252  273 toggle @Override
2253    List<CompilerInput> getInputsInOrder() {
2254  273 return Collections.<CompilerInput>unmodifiableList(inputs);
2255    }
2256   
2257    /**
2258    * Returns an unmodifiable view of the compiler inputs indexed by id.
2259    */
 
2260  0 toggle public Map<InputId, CompilerInput> getInputsById() {
2261  0 return Collections.unmodifiableMap(inputsById);
2262    }
2263   
2264    /**
2265    * Gets the externs in the order in which they are being processed.
2266    */
 
2267  273 toggle List<CompilerInput> getExternsInOrder() {
2268  273 return Collections.<CompilerInput>unmodifiableList(externs);
2269    }
2270   
2271    /**
2272    * Stores the internal compiler state just before optimization is performed.
2273    * This can be saved and restored in order to efficiently optimize multiple
2274    * different output targets without having to perform checking multiple times.
2275    *
2276    * NOTE: This does not include all parts of the compiler's internal state. In
2277    * particular, SourceFiles and CompilerOptions are not recorded. In
2278    * order to recreate a Compiler instance from scratch, you would need to
2279    * call {@code init} with the same arguments as in the initial creation before
2280    * restoring intermediate state.
2281    */
 
2282    public static class IntermediateState implements Serializable {
2283    private static final long serialVersionUID = 1L;
2284   
2285    Node externsRoot;
2286    private Node jsRoot;
2287    private List<CompilerInput> externs;
2288    private List<CompilerInput> inputs;
2289    private List<JSModule> modules;
2290    private PassConfig.State passConfigState;
2291    private JSTypeRegistry typeRegistry;
2292    private AbstractCompiler.LifeCycleStage lifeCycleStage;
2293    private Map<String, Node> injectedLibraries;
2294   
 
2295  0 toggle private IntermediateState() {}
2296    }
2297   
2298    /**
2299    * Returns the current internal state, excluding the input files and modules.
2300    */
 
2301  0 toggle public IntermediateState getState() {
2302  0 IntermediateState state = new IntermediateState();
2303  0 state.externsRoot = externsRoot;
2304  0 state.jsRoot = jsRoot;
2305  0 state.externs = externs;
2306  0 state.inputs = inputs;
2307  0 state.modules = modules;
2308  0 state.passConfigState = getPassConfig().getIntermediateState();
2309  0 state.typeRegistry = typeRegistry;
2310  0 state.lifeCycleStage = getLifeCycleStage();
2311  0 state.injectedLibraries = Maps.newLinkedHashMap(injectedLibraries);
2312   
2313  0 return state;
2314    }
2315   
2316    /**
2317    * Sets the internal state to the capture given. Note that this assumes that
2318    * the input files are already set up.
2319    */
 
2320  0 toggle public void setState(IntermediateState state) {
2321  0 externsRoot = state.externsRoot;
2322  0 jsRoot = state.jsRoot;
2323  0 externs = state.externs;
2324  0 inputs = state.inputs;
2325  0 modules = state.modules;
2326  0 passes = createPassConfigInternal();
2327  0 getPassConfig().setIntermediateState(state.passConfigState);
2328  0 typeRegistry = state.typeRegistry;
2329  0 setLifeCycleStage(state.lifeCycleStage);
2330   
2331  0 injectedLibraries.clear();
2332  0 injectedLibraries.putAll(state.injectedLibraries);
2333    }
2334   
 
2335  3 toggle @VisibleForTesting
2336    List<CompilerInput> getInputsForTesting() {
2337  3 return inputs;
2338    }
2339   
 
2340  2 toggle @VisibleForTesting
2341    List<CompilerInput> getExternsForTesting() {
2342  2 return externs;
2343    }
2344   
 
2345  332 toggle @Override
2346    boolean hasRegExpGlobalReferences() {
2347  332 return hasRegExpGlobalReferences;
2348    }
2349   
 
2350  69 toggle @Override
2351    void setHasRegExpGlobalReferences(boolean references) {
2352  69 hasRegExpGlobalReferences = references;
2353    }
2354   
 
2355  742 toggle @Override
2356    void updateGlobalVarReferences(Map<Var, ReferenceCollection> refMapPatch,
2357    Node collectionRoot) {
2358  742 Preconditions.checkState(collectionRoot.isScript()
2359    || collectionRoot.isBlock());
2360  742 if (globalRefMap == null) {
2361  273 globalRefMap = new GlobalVarReferenceMap(getInputsInOrder(),
2362    getExternsInOrder());
2363    }
2364  742 globalRefMap.updateGlobalVarReferences(refMapPatch, collectionRoot);
2365    }
2366   
 
2367  742 toggle @Override
2368    GlobalVarReferenceMap getGlobalVarReferences() {
2369  742 return globalRefMap;
2370    }
2371   
 
2372  126 toggle @Override
2373    CompilerInput getSynthesizedExternsInput() {
2374  126 if (synthesizedExternsInput == null) {
2375  56 synthesizedExternsInput = newExternInput(SYNTHETIC_EXTERNS);
2376    }
2377  126 return synthesizedExternsInput;
2378    }
2379   
 
2380  236 toggle @Override
2381    public double getProgress() {
2382  236 return progress;
2383    }
2384   
 
2385  0 toggle @Override
2386    String getLastPassName() {
2387  0 return lastPassName;
2388    }
2389   
 
2390  7456 toggle @Override
2391    void setProgress(double newProgress, String passName) {
2392  7456 this.lastPassName = passName;
2393  7456 if (newProgress > 1.0) {
2394  25 progress = 1.0;
2395    } else {
2396  7431 progress = newProgress;
2397    }
2398    }
2399   
2400    /**
2401    * Replaces one file in a hot-swap mode. The given JsAst should be made
2402    * from a new version of a file that already was present in the last compile
2403    * call. If the file is new, this will silently ignored.
2404    *
2405    * @param ast the ast of the file that is being replaced
2406    */
 
2407  0 toggle public void replaceScript(JsAst ast) {
2408  0 CompilerInput input = this.getInput(ast.getInputId());
2409  0 if (!replaceIncrementalSourceAst(ast)) {
2410  0 return;
2411    }
2412  0 Node originalRoot = input.getAstRoot(this);
2413   
2414  0 processNewScript(ast, originalRoot);
2415    }
2416   
2417    /**
2418    * Adds a new Script AST to the compile state. If a script for the same file
2419    * already exists the script will not be added, instead a call to
2420    * #replaceScript should be used.
2421    *
2422    * @param ast the ast of the new file
2423    */
 
2424  0 toggle public void addNewScript(JsAst ast) {
2425  0 if (!addNewSourceAst(ast)) {
2426  0 return;
2427    }
2428  0 Node emptyScript = new Node(Token.SCRIPT);
2429  0 InputId inputId = ast.getInputId();
2430  0 emptyScript.setInputId(inputId);
2431  0 emptyScript.setStaticSourceFile(
2432    SourceFile.fromCode(inputId.getIdName(), ""));
2433   
2434  0 processNewScript(ast, emptyScript);
2435    }
2436   
 
2437  0 toggle private void processNewScript(JsAst ast, Node originalRoot) {
2438  0 Node js = ast.getAstRoot(this);
2439  0 Preconditions.checkNotNull(js);
2440   
2441  0 runHotSwap(originalRoot, js, this.getCleanupPassConfig());
2442    // NOTE: If hot swap passes that use GlobalNamespace are added, we will need
2443    // to revisit this approach to clearing GlobalNamespaces
2444  0 runHotSwapPass(null, null, ensureDefaultPassConfig().garbageCollectChecks);
2445   
2446  0 this.getTypeRegistry().clearNamedTypes();
2447  0 this.removeSyntheticVarsInput();
2448   
2449  0 runHotSwap(originalRoot, js, this.ensureDefaultPassConfig());
2450    }
2451   
2452    /**
2453    * Execute the passes from a PassConfig instance over a single replaced file.
2454    */
 
2455  0 toggle private void runHotSwap(
2456    Node originalRoot, Node js, PassConfig passConfig) {
2457  0 for (PassFactory passFactory : passConfig.getChecks()) {
2458  0 runHotSwapPass(originalRoot, js, passFactory);
2459    }
2460    }
2461   
 
2462  0 toggle private void runHotSwapPass(
2463    Node originalRoot, Node js, PassFactory passFactory) {
2464  0 HotSwapCompilerPass pass = passFactory.getHotSwapPass(this);
2465  0 if (pass != null) {
2466  0 logger.info("Performing HotSwap for pass " + passFactory.getName());
2467  0 pass.hotSwapScript(js, originalRoot);
2468    }
2469    }
2470   
 
2471  0 toggle private PassConfig getCleanupPassConfig() {
2472  0 return new CleanupPasses(getOptions());
2473    }
2474   
 
2475  0 toggle private void removeSyntheticVarsInput() {
2476  0 String sourceName = Compiler.SYNTHETIC_EXTERNS;
2477  0 removeExternInput(new InputId(sourceName));
2478    }
2479   
 
2480  26 toggle @Override
2481    Node ensureLibraryInjected(String resourceName) {
2482  26 if (injectedLibraries.containsKey(resourceName)) {
2483  0 return null;
2484    }
2485   
2486    // All libraries depend on js/base.js
2487  26 boolean isBase = "base".equals(resourceName);
2488  26 if (!isBase) {
2489  13 ensureLibraryInjected("base");
2490    }
2491   
2492  26 Node firstChild = loadLibraryCode(resourceName).removeChildren();
2493  26 Node lastChild = firstChild.getLastSibling();
2494   
2495  26 Node parent = getNodeForCodeInsertion(null);
2496  26 if (isBase) {
2497  13 parent.addChildrenToFront(firstChild);
2498    } else {
2499  13 parent.addChildrenAfter(
2500    firstChild, injectedLibraries.get("base"));
2501    }
2502  26 reportCodeChange();
2503   
2504  26 injectedLibraries.put(resourceName, lastChild);
2505  26 return lastChild;
2506    }
2507   
2508    /** Load a library as a resource */
 
2509  52 toggle @VisibleForTesting
2510    Node loadLibraryCode(String resourceName) {
2511  52 String originalCode;
2512  52 try {
2513  52 originalCode = CharStreams.toString(new InputStreamReader(
2514    Compiler.class.getResourceAsStream(
2515    String.format("js/%s.js", resourceName)),
2516    Charsets.UTF_8));
2517    } catch (IOException e) {
2518  0 throw new RuntimeException(e);
2519    }
2520   
2521  52 return Normalize.parseAndNormalizeSyntheticCode(
2522    this, originalCode,
2523    String.format("jscomp_%s_", resourceName));
2524    }
2525   
2526    /** Returns the compiler version baked into the jar. */
 
2527  2 toggle public static String getReleaseVersion() {
2528  2 ResourceBundle config = ResourceBundle.getBundle(CONFIG_RESOURCE);
2529  2 return config.getString("compiler.version");
2530    }
2531   
2532    /** Returns the compiler date baked into the jar. */
 
2533  2 toggle public static String getReleaseDate() {
2534  2 ResourceBundle config = ResourceBundle.getBundle(CONFIG_RESOURCE);
2535  2 return config.getString("compiler.date");
2536    }
2537   
2538    /**
2539    * {@inheritDoc}
2540    */
 
2541  29447 toggle @Override
2542    public void setOldParseTree(String sourceName, AstRoot oldAst) {
2543    }
2544   
2545    /**
2546    * {@inheritDoc}
2547    */
 
2548  0 toggle @Override
2549    public AstRoot getOldParseTreeByName(String sourceName) {
2550  0 return null;
2551    }
2552    }